import java.sql.*;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @author Vincent Bantjes
 */
public class CollapsePreConditions extends PreConditions
{
    static final String DRIVER = "org.sqlite.JDBC";
    private String DATABASE_URL = "jdbc:sqlite:infinitysolutions.db";
    private ResultSet result;
    private ResultSet result2 = null;
    private Connection connection;
    private Statement statement;
    String databaseName = "infinitysolutions";
    
    /**
     * The default constructor. This constructor will connect to a database named infinitysolutions with username: root and password: root
     */    
    CollapsePreConditions()
    {
    }
    
    /**
     * The connect method. This will connect to the database with the parameters specified in the constructor.
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    void connect() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
    {
	Class.forName(DRIVER);
	connection = DriverManager.getConnection(DATABASE_URL+databaseName);
    }
    
    
    /**
     * The disconnect method. This will close the database connection.
     * @throws SQLException
     */
    void disconnect() throws SQLException
    {
    	connection.close();
    }
    
    /**
     * Checks if the given class extends/implements the given class name.
     * @param className String. The class that is checked if it is a superclass.
     * @param otherClass String. The class that is being checked if it is a subclass of the given class name. 
     * @return 
     */
    public boolean isSubclass(String className, String otherClass)
    {
        Scanner st = new Scanner(otherClass);
        String current ="";
        String finalstr = "";
        boolean fb = true;
        boolean bc = true;
        boolean quoteFlag = true;
        boolean extend = true;
        boolean implement = true;
        int counter = 0;
        
        while(st.hasNext())
        {
            finalstr = "";
            current = st.nextLine();
            if(current.length() >= 2 && current.trim().length() >= 2 && current.trim().substring(0, 2).equals("//"))
            {
                
            }
            else
            {

                for(int i = 0; i < current.length() ;i++)
                {
                    if(current.length() >= i+2 && current.substring(i,i+2).equals("/*") && fb == true && quoteFlag == true)
                    {
                        fb = false;
                        quoteFlag = false;
                        bc = false;
                    }


                    if(current.length() >= i+2 && current.substring(i,i+2).equals("*/"))
                    {
                        fb = true;
                        quoteFlag = true;
                        bc = true;
                        i++;
                        i++;
                    }
                    if(i < current.length() && current.length() >= i+2 && current.substring(i,i+2).equals("/*") && quoteFlag == true)
                    {
                        fb = false;
                    }
                    if(counter == 2)
                        quoteFlag = true;
                    if(i < current.length() && current.charAt(i) == '"' && fb == true)
                    {
                        if(current.charAt(i-1) != '\\')
                                counter++;
                        quoteFlag = false;
                    }



                    if(current.length() >= i+2 && current.substring(i,i+2).equals("//") && bc == true && quoteFlag == true)
                    {
                        break;
                    }                   
                    if(i < current.length() &&fb == true && quoteFlag == true && bc == true)
                    {
                        finalstr+= current.charAt(i);
                    }
                    

                }
                //System.out.println(finalstr);
                if(extend == false)
                {
                    if(className.length() <= finalstr.trim().length() && finalstr.trim().substring(0,className.length()).contains(className))
                    {
                        return true;
                    }
                    extend = true;
                }

                if(implement == false)
                {
                    if(className.length() <= finalstr.trim().length() && finalstr.trim().substring(0,className.length()).contains(className))
                    {
                        return true;
                    }
                    implement = true;
                    return false;
                }
                if(finalstr.contains("extends"))
                {
                    if(finalstr.substring(finalstr.indexOf("extends")+7, finalstr.length()).trim().equals(""))
                    {
                        extend = false;
                    }
                    else
                    {
                        if(finalstr.substring(finalstr.indexOf("extends")+7, finalstr.length()).contains(className))
                        {
                            if(finalstr.contains("implements") && finalstr.indexOf(className) < finalstr.indexOf("implements"))
                                return true;
                            else if(finalstr.contains("implements") && finalstr.indexOf(className) > finalstr.indexOf("implements"))
                                return true;
                            else    
                            {
                                return true;
                            }
                        }
                    }
                }

                if(finalstr.contains("implements"))
                {
                    if(finalstr.substring(finalstr.indexOf("implements")+10, finalstr.length()).trim().equals(""))
                    {
                        implement = false;
                    }
                    else
                    {
                        if(finalstr.substring(finalstr.indexOf("implements"), finalstr.length()).contains(className))
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    /**
     * The getImplements method,Returns an array containing classes implemented
     * @return an array of strings 
     * @param className String. The class name
     */
    public Vector<String> getImplements(String className)
    {
        Vector<String> arry = new Vector<String>();
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT DestinationClass,Label   FROM extends_implement WHERE SourceClass ='" +className+ "' AND Label = 'implements';" );  
            while(result.next())
            {
                arry.add(result.getString(1));    
            }            
        } 
        catch (Exception ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return arry;
    }

    /**
     * The getClassLineNum method,gets the linenumber of where the class is declared
     * @return an int
     * @param className String. The class to get the line number for
     */
    public int getClassLineNum(String className)
    {
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT LineNum FROM class WHERE className ='" +className+ "';" );  
            while(result.next())
            {
                return result.getInt(1);
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }        
        return -1;
    }    
    
    
    /**
     * The subExtendsSuperClass method,Checks if the sub class extends the superclass
     * @return a boolean
     * @param class1 String. The first class name
     * @param class2 String. The second class name
     */
    public boolean subExtendsSuperClass(String class1,String class2)
    {
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT DestinationClass FROM extends_implement WHERE SourceClass ='" +class2+ "' AND Label = 'extends';" );  
            while(result.next())
            {
                if(result.getString(1).equals(class1))
                {
                    return true;
                }
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }        
        return false;
    }
    
    
     /**
     * The getAllImports method,Returns an array containing the same imports used
     * @return an Vector of strings 
     * @param className String. The class name which we should get the imports from
     */
    public Vector<String> getImports(String className)
    {
        Vector<String> arry = new Vector<String>();
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT importName   FROM imports WHERE className  ='" +className+ "';" );  
            while(result.next())
            {
                arry.add(result.getString(1));
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }        
        return arry;
    }    

     /**
     * The superExtends method,Returns a string which the super class extend
     * @return a String which the class name which the superclass extends
     * @param superClass String. The first class name
     */
    public String superExtends(String superClass)
    {
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT DestinationClass,Label   FROM extends_implement WHERE SourceClass ='" +superClass+ "' AND Label = 'extends';" );  
            while(result.next())
            {
               return result.getString(1);//class extended by superClass                  
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return "";
    }
    
    /**
     * The extDifferentClass method,Returns true if class 1 and 2 extends different classes
     * @return a boolean
     * @param class1 String. The first class name
     * @param class2 String. The second class name
     */
    public boolean extDifferentClass(String class1,String class2)
    {
        int count = 0;
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT DestinationClass,Label   FROM extends_implement WHERE SourceClass ='" +class1+ "' AND Label = 'extends';" );  
            statement = connection.createStatement();
            result2= statement.executeQuery("SELECT DestinationClass,Label   FROM extends_implement WHERE SourceClass ='" +class2+ "' AND Label = 'extends';" );
            while(result2.next())
            {
                while(result.next())
                {
                    count++;
                    if(result.getString(1).equals(result2.getString(1)))
                    {
                        return false;//true if extend from same class
                    }
                }
                statement = connection.createStatement();
                result = statement.executeQuery("SELECT MethodName,MethodParameterList  FROM method WHERE ClassName ='" +class1+ "' AND Label = 'extends';" );
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        if(count == 0)
        {
            return false;
        }
        return true;
    }    
    
    /**
     * The SuperHasMoreSub method,Returns true if superclass has more than one sub classes
     * @return a boolean
     * @param superClass String. The super class name to check
     */
    public boolean SuperHasMoreSub(String superClass)
    {
        int counter = 0;
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT SourceClass FROM  extends_implement WHERE DestinationClass ='" +superClass+ "' AND Label = 'extends';" );
            while(result.next())
            {
                counter++;
                if(counter == 2)
                {
                    return true;
                }
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return false;
    }
    
    /**
     * The constructorLineNum method,Returns an array of line numbers of constructors
     * @return a boolean
     * @param className String. The class name to check look for constructors
     */
    public Vector<Integer> constructorLineNum(String className)
    {
        Vector<Integer> lineNum = new Vector<Integer>();
        try 
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT LineNum FROM  method WHERE ClassName  ='" +className+ "' AND AccMode = 'constructor';" );
            while(result.next())
            {
                lineNum.add(result.getInt(1));
            }
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return lineNum;
    }
    
    /**
     * The methodClash method,Returns an array containing the the method clash objects
     * @return MethodClash objects
     * @param class1 String. The first class name
     * @param class2 String. The second class name
     */
    public Vector<MethodClash> methodClash(String class1,String class2)
    {      
        Vector<MethodClash> method = new Vector<MethodClash>();
        try
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT MethodName,MethodParameterList,LineNum   FROM method WHERE ClassName ='" +class1+ "';" );
            statement = connection.createStatement();
            result2 = statement.executeQuery("SELECT MethodName,MethodParameterList,LineNum   FROM method WHERE ClassName ='" +class2+ "';" );
            ResultSet field1 = null;
            ResultSet field2 = null;
            boolean flag = false;
            while(result.next())
            {
                while(result2.next())
                {                
                    if(result2.getString(1).equals(result.getString(1)))
                    {
                        StringTokenizer t1 = new StringTokenizer(result.getString(2));
                        StringTokenizer t2 = new StringTokenizer(result2.getString(2));
                        if(t1.countTokens() == t2.countTokens())
                        {
                            while(t1.hasMoreTokens())
                            {
                                statement = connection.createStatement();
                                field1 = statement.executeQuery("SELECT FieldType FROM field WHERE FieldID ='" +t1.nextToken()+ "';" );
                                statement = connection.createStatement();
                                field2 = statement.executeQuery("SELECT FieldType FROM field WHERE FieldID ='" +t2.nextToken()+ "';" );
                                while(field1.next())
                                {
                                    while(field2.next())
                                    {
                                        if(field1.getString(1).equals(field2.getString(1)) == false)
                                        {
                                            flag = true;
                                        }
                                    }
                                }
                            }
                            if(flag == false)
                            {
                                method.add(new MethodClash(result.getString(3),result2.getString(3),result.getString(1)));
                            }
                            flag = false;
                        }
                    }
                }
                statement = connection.createStatement();
                result2 = statement.executeQuery("SELECT MethodName,MethodParameterList,LineNum  FROM method WHERE ClassName ='" +class2+ "';" );
            }
        }
        catch(SQLException sql)
        {
            JOptionPane.showMessageDialog(null, sql.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return method;
    }
    
     /**
     * The fieldClash method,Returns an array containing the the field clash objects
     * @return fieldClash objects
     * @param class1 String. The first class name
     * @param class2 String. The second class name
     */
    public Vector<FieldClash> fieldClash(String class1,String class2)
    {      
        Vector<FieldClash> field = new Vector<FieldClash>();
        try
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT FieldName,LineNum FROM field WHERE ClassName ='" +class1+ "' AND VariableScope = 'global';" );
            statement = connection.createStatement();
            result2 = statement.executeQuery("SELECT FieldName,LineNum FROM field WHERE ClassName ='" +class2+ "' AND VariableScope = 'global';" );
            while(result.next())
            {
                while(result2.next())
                {                
                    if(result2.getString(1).equals(result.getString(1)))
                    {
                        field.add(new FieldClash(result.getString(2),result2.getString(2),result.getString(1)));
                    }
                 }
                statement = connection.createStatement();
                result2 = statement.executeQuery("SELECT FieldName,LineNum FROM field WHERE ClassName ='" +class2+ "' AND VariableScope = 'global';" );
            }
        }
        catch(SQLException sql)
        {
            JOptionPane.showMessageDialog(null, sql.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return field;
    }

    /**
     * This method returns the superclass of a given subclass if the subclass extends it.
     * @return String. The superclass name, or "" if there is none.
     * @param className String. The subclass name.
     */
    public String getSuperclass(String className)
    {
        try 
        {
            System.out.println("1");
            try 
            {
                connect();
            } 
            catch(Exception ex) 
            {
                Logger.getLogger(CollapsePreConditions.class.getName()).log(Level.SEVERE, null, ex);
            } 
            System.out.println(connection);
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT DestinationClass FROM  extends_implement WHERE SourceClass ='" +className+ "' AND Label = 'extends';");
            while(result.next())
            {
                return result.getString(1);
            }
            System.out.println("2");
            statement = connection.createStatement();
            result2 = statement.executeQuery("SELECT DestinationClass FROM  extends_implement WHERE SourceClass ='" +className+ "' AND Label = 'implements';");
            int counter = 0;
            String value = "";
            System.out.println("3");
            while(result2.next())
            {
                value = result2.getString(1);
                counter++;
                if(counter > 1)
                {
                    return "There was more than one implementedclass.";
                }
            }
            System.out.println("4");
            if(counter == 1)
            {
                return value;
            }
            return "No superclass.";
        } 
        catch (SQLException ex) 
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return "";
    }
    
    /**
     * The abstractCheck method,Checks if the class is abstract
     * @return a boolean
     * @param className String.
     */
    public boolean abstractCheck(String className)
    {
        try
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT ClassName FROM  class WHERE ClassName ='" +className+ "' AND AccMode = 'abstract';" );
            while(result.next())
            {
                return true;
            }
        }
        catch (SQLException ex)
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return false;
    } 
   
   /**
     * The interfaceCheck method,Checks if the class is interface
     * @return a boolean
     * @param className String.
     */
    public boolean interfaceCheck(String className)
    {
        try
        {
            statement = connection.createStatement();
            result = statement.executeQuery("SELECT ClassName FROM  class WHERE ClassName ='" +className+ "' AND AccMode = 'interface';" );
            while(result.next())
            {
                return true;
            }
        }
        catch (SQLException ex)
        {
            JOptionPane.showMessageDialog(null, ex.toString(), "Collapse Hierarchy Preconditions", JOptionPane.ERROR_MESSAGE);
        }
        return false;
    }
}
